
using System;
using CLUBsInterpreter.AST;
using CLUBsInterpreter.ObjectTypes;

namespace CLUBsInterpreter {



public class Parser {
	public const int _EOF = 0;
	public const int _ident = 1;
	public const int _isop = 2;
	public const int _notop = 3;
	public const int _number = 4;
	public const int _scope = 5;
	public const int maxT = 42;

	const bool T = true;
	const bool x = false;
	const int minErrDist = 2;
	
	public Scanner scanner;
	public Errors  errors;

	public Token t;    // last recognized token
	public Token la;   // lookahead token
	int errDist = minErrDist;

internal CLUBsNode ast;
	internal IdentificationTable idt;
	
// LL(1) Conflict Resolution Methods
	private bool IsAssignment()
	{
		Token t = scanner.Peek();
		while (t.kind == _ident || t.kind == _number || t.kind == _scope || t.val == "[" || t.val == "]")
		{
			t = scanner.Peek();
		}
		return (t.val == "=");
	}
	private bool IsCall()
	{
		Token t = scanner.Peek();
		while (t.kind == _ident || t.kind == _number || t.kind == _scope || t.val == "[" || t.val == "]")
		{
			if (t.val == "not" || t.val == "is") return false;
			t = scanner.Peek();
		}
		return (t.val == "(");
	}

// ##############################################################
//                            SCANNER
// ##############################################################



	public Parser(Scanner scanner) {
		this.scanner = scanner;
		errors = new Errors();
	}

	void SynErr (int n) {
		if (errDist >= minErrDist) errors.SynErr(la.line, la.col, n);
		errDist = 0;
	}

	public void SemErr (string msg) {
		if (errDist >= minErrDist) errors.SemErr(t.line, t.col, msg);
		errDist = 0;
	}
	
	void Get () {
		for (;;) {
			t = la;
			la = scanner.Scan();
			if (la.kind <= maxT) { ++errDist; break; }

			la = t;
		}
	}
	
	void Expect (int n) {
		if (la.kind==n) Get(); else { SynErr(n); }
	}
	
	bool StartOf (int s) {
		return set[s, la.kind];
	}
	
	void ExpectWeak (int n, int follow) {
		if (la.kind == n) Get();
		else {
			SynErr(n);
			while (!StartOf(follow)) Get();
		}
	}


	bool WeakSeparator(int n, int syFol, int repFol) {
		int kind = la.kind;
		if (kind == n) {Get(); return true;}
		else if (StartOf(repFol)) {return false;}
		else {
			SynErr(n);
			while (!(set[syFol, kind] || set[repFol, kind] || set[0, kind])) {
				Get();
				kind = la.kind;
			}
			return StartOf(syFol);
		}
	}

	
	void CLUBs() {
		while (la.kind == 10) {
			Library();
		}
		if (la.kind == 6) {
			Game();
			ast.IsLibrary = false; 
		} else if (la.kind == 9) {
			LibraryDef();
			ast.IsLibrary = true; 
		} else SynErr(43);
	}

	void Library() {
		LibraryNode node = new LibraryNode();
		node.Parent = ast;
		ast.Libraries.Add(node); 
		Expect(10);
		Ident(out node.Name);
		Expect(11);
	}

	void Game() {
		Expect(6);
		Expect(7);
		while (StartOf(1)) {
			Definition();
		}
		StartAction();
		while (StartOf(1)) {
			Definition();
		}
		Expect(8);
	}

	void LibraryDef() {
		Expect(9);
		Expect(7);
		while (StartOf(1)) {
			Definition();
		}
		Expect(8);
	}

	void Definition() {
		IdentifierType type; string name; 
		if (StartOf(2)) {
			Type(out type);
			Ident(out name);
			DefinitionNode node = DefinitionNode.GenerateNode(type);
			node.Name = name;
			node.BlockName = name;
			node.DefinitionType = type;
			if (idt.ContainsKey(name))
			{
				SemErr("Duplicate definition of "+name);
				return;
			}
			else
			{
				ast.Definitions.Add(node);
				idt[name] = new Identification();
				idt[name].IdentType = type;
				idt[name].Value = CLUBsObject.GenerateObject(type, node);
				node.ObjectReference = idt[name].Value;
			}
			idt = idt.OpenScope(node, true); 
			Expect(7);
			while (StartOf(3)) {
				Statement(node);
			}
			Expect(8);
			idt = idt.CloseScope(); 
		} else if (la.kind == 12) {
			ActionDefinition();
		} else SynErr(44);
	}

	void StartAction() {
		ast.StartAction = new ActionDefNode(); 
		Expect(16);
		ast.StartAction.BlockName = "__start";
		ast.StartAction.Name = "__start";
		ast.StartAction.DefinitionType = IdentifierType.Action;
		idt["__start"] = new Identification();
		idt["__start"].IdentType = IdentifierType.Action;
		idt["__start"].Value = CLUBsObject.GenerateObject(IdentifierType.Action, ast.StartAction);
		idt = idt.OpenScope(ast.StartAction, true); 
		Expect(7);
		while (StartOf(3)) {
			Statement(ast.StartAction);
		}
		Expect(8);
		idt = idt.CloseScope(); 
	}

	void Ident(out string name) {
		Expect(1);
		name = t.val; 
	}

	void Type(out IdentifierType type) {
		type = IdentifierType.Unset; 
		switch (la.kind) {
		case 27: {
			Get();
			type = IdentifierType.Board; 
			break;
		}
		case 28: {
			Get();
			type = IdentifierType.Cell; 
			break;
		}
		case 29: {
			Get();
			type = IdentifierType.Piece; 
			break;
		}
		case 30: {
			Get();
			type = IdentifierType.Player; 
			break;
		}
		case 31: {
			Get();
			type = IdentifierType.Round; 
			break;
		}
		case 32: {
			Get();
			type = IdentifierType.Turn; 
			break;
		}
		default: SynErr(45); break;
		}
	}

	void Statement(BlockNode block) {
		StatementNode stmt = null; 
		if (StartOf(4)) {
			ControlStructure(out stmt);
		} else if (StartOf(5)) {
			if (IsAssignment()) {
				AssignStatement(out stmt);
			} else if (la.kind == 1) {
				CallStatement(out stmt);
			} else {
				DeclStatement(out stmt);
			}
			Expect(11);
		} else SynErr(46);
		stmt.LineNumber = (uint)t.line;
		block.Statements.Add(stmt); 
	}

	void ActionDefinition() {
		ActionDefNode node = new ActionDefNode();
		CLUBsAction act = new CLUBsAction();
		node.Action = act;
		node.DefinitionType = IdentifierType.Action; 
		Expect(12);
		Ident(out node.Name);
		node.BlockName = node.Name;
		if (idt.ContainsKey(node.Name))
		{
			SemErr("Duplicate definition of "+node.Name);
			return;
		}
		else
		{
			ast.Definitions.Add(node);
			idt[node.Name] = new Identification();
			idt[node.Name].IdentType = IdentifierType.Action;
			idt[node.Name].Value = act;
			node.ObjectReference = act;
		} 
		if (la.kind == 13) {
			IdentifierType retType; 
			Get();
			IdentType(out retType);
			node.Action.ReturnType = retType; 
		}
		if (la.kind == 14) {
			Get();
			IdentifierType paramType; string paramName; 
			IdentType(out paramType);
			Ident(out paramName);
			act.ParameterTypes[paramName] = paramType; 
			while (la.kind == 15) {
				Get();
				IdentType(out paramType);
				Ident(out paramName);
				act.ParameterTypes[paramName] = paramType; 
			}
		}
		idt = idt.OpenScope(node, true); 
		Expect(7);
		while (StartOf(3)) {
			Statement(node);
		}
		Expect(8);
		idt = idt.CloseScope(); 
	}

	void IdentType(out IdentifierType type) {
		type = IdentifierType.Unset; 
		switch (la.kind) {
		case 27: {
			Get();
			type = IdentifierType.Board; 
			break;
		}
		case 28: {
			Get();
			type = IdentifierType.Cell; 
			break;
		}
		case 29: {
			Get();
			type = IdentifierType.Piece; 
			break;
		}
		case 30: {
			Get();
			type = IdentifierType.Player; 
			break;
		}
		case 31: {
			Get();
			type = IdentifierType.Round; 
			break;
		}
		case 32: {
			Get();
			type = IdentifierType.Turn; 
			break;
		}
		case 12: {
			Get();
			type = IdentifierType.Action; 
			break;
		}
		case 33: {
			Get();
			type = IdentifierType.Integer; 
			break;
		}
		case 34: {
			Get();
			type = IdentifierType.String; 
			break;
		}
		case 35: {
			Get();
			type = IdentifierType.Boolean; 
			break;
		}
		default: SynErr(47); break;
		}
	}

	void ControlStructure(out StatementNode stmt) {
		stmt = null; 
		if (la.kind == 20) {
			If(out stmt);
		} else if (la.kind == 21) {
			Check(out stmt);
		} else if (la.kind == 24) {
			While(out stmt);
		} else if (la.kind == 25) {
			For(out stmt);
		} else SynErr(48);
	}

	void AssignStatement(out StatementNode outStmt) {
		AssignStmtNode stmt = new AssignStmtNode(); 
		if (la.kind == 1) {
			CompIdent(out stmt.Ident);
		} else if (StartOf(6)) {
			StatementNode tmpStmt; 
			DeclStatement(out tmpStmt);
			stmt.Declaration = (DeclStmtNode)tmpStmt;
			stmt.Ident = stmt.Declaration.Ident; 
		} else SynErr(49);
		Expect(17);
		Expression(out stmt.Expression);
		outStmt = stmt; 
	}

	void CallStatement(out StatementNode outStmt) {
		CallStmtNode stmt = new CallStmtNode();
		ExpressionNode curExp; 
		CompIdent(out stmt.Ident);
		Expect(18);
		if (StartOf(7)) {
			Expression(out curExp);
			stmt.Parameters.Add(curExp); 
			while (la.kind == 15) {
				Get();
				Expression(out curExp);
				stmt.Parameters.Add(curExp); 
			}
		}
		Expect(19);
		stmt.LineNumber = (uint)t.line;
		outStmt = stmt; 
	}

	void DeclStatement(out StatementNode outStmt) {
		DeclStmtNode stmt = new DeclStmtNode();
		Identifier ident = new Identifier();
		ident.SubIdents.Add(new ArrayIdentifier()); 
		IdentType(out stmt.InferredType);
		Ident(out ident.SubIdents[0].Name);
		ident.SubIdents[0].InferredType = stmt.InferredType;
		ident.InferredType = stmt.InferredType;
		stmt.Ident = ident;
		string name = ident.SubIdents[0].Name;
		if (idt.ContainsKey(name))
		{
			SemErr("Duplicate definition of "+name);
		}
		else
		{
			idt[name] = new Identification();
			idt[name].IdentType = ident.InferredType;
			idt[name].Value = null;
			//ident.Reference = idt[name].Value;
			//ident.SubIdents[0].Reference = idt[name].Value;
		}
		stmt.LineNumber = (uint)t.line;
		outStmt = stmt; 
	}

	void CompIdent(out Identifier ident) {
		ident = new Identifier();
		ArrayIdentifier arrIdent; 
		ArrayIdent(out arrIdent);
		ident.SubIdents.Add(arrIdent); 
		while (la.kind == 5) {
			Get();
			ArrayIdent(out arrIdent);
			ident.SubIdents.Add(arrIdent); 
		}
		ident.LineNumber = (uint)t.line; 
	}

	void Expression(out ExpressionNode firstExp) {
		Operator op = Operator.Invalid; ExpressionNode secondExp; BinaryExpNode newExp; 
		if (StartOf(8)) {
			Op(out op);
		}
		SubExpression(out firstExp);
		if (op != Operator.Invalid)
		{
			UnaryExpNode uExp = new UnaryExpNode();
			uExp.Op = op;
			uExp.Exp = firstExp;
			uExp.LineNumber = (uint)t.line;
			firstExp = uExp;
		} 
		while (StartOf(8)) {
			Op(out op);
			SubExpression(out secondExp);
			newExp = new BinaryExpNode();
			newExp.Exp1 = firstExp;
			newExp.Op = op;
			newExp.Exp2 = secondExp;
			newExp.LineNumber = (uint)t.line;
			firstExp = newExp; 
		}
	}

	void If(out StatementNode outStmt) {
		IfStmtNode stmt = new IfStmtNode();
		stmt.Body = new BlockNode(); 
		Expect(20);
		Expect(18);
		Expression(out stmt.Condition);
		Expect(19);
		idt = idt.OpenScope(stmt.Body, true); 
		Expect(7);
		while (StartOf(3)) {
			Statement(stmt.Body);
		}
		Expect(8);
		idt = idt.CloseScope();
		outStmt = stmt; 
	}

	void Check(out StatementNode outStmt) {
		CheckStmtNode stmt = new CheckStmtNode(); 
		Expect(21);
		Expect(18);
		Expression(out stmt.CheckedExpression);
		Expect(19);
		Expect(7);
		CaseNode curCase; 
		while (la.kind == 22) {
			Case(out curCase);
			stmt.Cases.Add(curCase); 
		}
		Expect(8);
		outStmt = stmt; 
	}

	void While(out StatementNode outStmt) {
		WhileStmtNode stmt = new WhileStmtNode();
		stmt.Body = new BlockNode(); 
		Expect(24);
		Expect(18);
		Expression(out stmt.Condition);
		Expect(19);
		idt = idt.OpenScope(stmt.Body, true); 
		Expect(7);
		while (StartOf(3)) {
			Statement(stmt.Body);
		}
		Expect(8);
		idt = idt.CloseScope();
		outStmt = stmt; 
	}

	void For(out StatementNode outStmt) {
		ForStmtNode stmt = new ForStmtNode();
		stmt.Body = new BlockNode();
		BlockNode tmpBlock = new BlockNode(); 
		Expect(25);
		Expect(18);
		Statement(tmpBlock);
		Expect(11);
		Expression(out stmt.Condition);
		Expect(11);
		Statement(tmpBlock);
		stmt.Initialize = tmpBlock.Statements[0];
		stmt.Increment = tmpBlock.Statements[1]; 
		Expect(19);
		idt = idt.OpenScope(stmt.Body, true); 
		Expect(7);
		while (StartOf(3)) {
			Statement(stmt.Body);
		}
		Expect(8);
		idt = idt.CloseScope();
		outStmt = stmt; 
	}

	void Case(out CaseNode outCase) {
		outCase = new CaseNode();
		outCase.LineNumber = (uint)t.line; 
		Expect(22);
		Expression(out outCase.MatchExpression);
		Expect(23);
		idt = idt.OpenScope(outCase, true); 
		while (StartOf(3)) {
			Statement(outCase);
		}
		idt = idt.CloseScope(); 
	}

	void Op(out Operator op) {
		op = Operator.Invalid; 
		switch (la.kind) {
		case 38: {
			Get();
			op = Operator.Plus; 
			break;
		}
		case 39: {
			Get();
			op = Operator.Minus; 
			break;
		}
		case 40: {
			Get();
			op = Operator.Mult; 
			break;
		}
		case 41: {
			Get();
			op = Operator.Div; 
			break;
		}
		case 2: {
			Get();
			op = Operator.Is; 
			break;
		}
		case 3: {
			Get();
			op = Operator.Not; 
			break;
		}
		default: SynErr(50); break;
		}
	}

	void SubExpression(out ExpressionNode exp) {
		exp = null; 
		if (IsCall()) {
			CallExpNode cexp = new CallExpNode();
			StatementNode stmt; 
			CallStatement(out stmt);
			cexp.Call = (CallStmtNode)stmt;
			exp = cexp; 
		} else if (la.kind == 26) {
			StringExpNode sexp = new StringExpNode();
			sexp.Value = ""; 
			Get();
			while (StartOf(9)) {
				Get();
				sexp.Value += t.val; 
			}
			Expect(26);
			exp = sexp; 
		} else if (la.kind == 4) {
			IntegerExpNode iexp = new IntegerExpNode(); int tempInt; 
			Number(out tempInt);
			iexp.Value = tempInt; exp = iexp; 
		} else if (la.kind == 1) {
			VNameExpNode vnexp = new VNameExpNode(); 
			CompIdent(out vnexp.Ident);
			exp = vnexp; 
		} else if (la.kind == 18) {
			ParenthesisExpNode pexp = new ParenthesisExpNode(); 
			Get();
			Expression(out pexp.Exp);
			Expect(19);
			exp = pexp; 
			exp.LineNumber = (uint)t.line; 
		} else SynErr(51);
	}

	void Number(out int num) {
		Expect(4);
		num = int.Parse(t.val); 
	}

	void ArrayIdent(out ArrayIdentifier ident) {
		ident = new ArrayIdentifier(); 
		Ident(out ident.Name);
		int num; 
		while (la.kind == 36) {
			Get();
			Number(out num);
			Expect(37);
			ident.Indices.Add(num); 
		}
		ident.LineNumber = (uint)t.line; 
	}



	public void Parse() {
		la = new Token();
		la.val = "";		
		Get();
		CLUBs();
		Expect(0);

	}
	
	static readonly bool[,] set = {
		{T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x},
		{x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,T, T,x,x,x, x,x,x,x, x,x,x,x},
		{x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,T, T,x,x,x, x,x,x,x, x,x,x,x},
		{x,T,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,x,x,x, T,T,x,x, T,T,x,T, T,T,T,T, T,T,T,T, x,x,x,x, x,x,x,x},
		{x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,T,x,x, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x},
		{x,T,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,T, T,T,T,T, x,x,x,x, x,x,x,x},
		{x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,T, T,T,T,T, x,x,x,x, x,x,x,x},
		{x,T,T,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,T,x, x,x,x,x, x,x,T,x, x,x,x,x, x,x,x,x, x,x,T,T, T,T,x,x},
		{x,x,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,T,T, T,T,x,x},
		{x,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,x,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,x}

	};
} // end Parser


public class Errors {
	public int count = 0;                                    // number of errors detected
	public System.IO.TextWriter errorStream = Console.Out;   // error messages go to this stream
	public string errMsgFormat = "-- line {0} col {1}: {2}"; // 0=line, 1=column, 2=text

	public virtual void SynErr (int line, int col, int n) {
		string s;
		switch (n) {
			case 0: s = "EOF expected"; break;
			case 1: s = "ident expected"; break;
			case 2: s = "isop expected"; break;
			case 3: s = "notop expected"; break;
			case 4: s = "number expected"; break;
			case 5: s = "scope expected"; break;
			case 6: s = "\"game\" expected"; break;
			case 7: s = "\"{\" expected"; break;
			case 8: s = "\"}\" expected"; break;
			case 9: s = "\"library\" expected"; break;
			case 10: s = "\"using\" expected"; break;
			case 11: s = "\";\" expected"; break;
			case 12: s = "\"action\" expected"; break;
			case 13: s = "\"gives\" expected"; break;
			case 14: s = "\"takes\" expected"; break;
			case 15: s = "\",\" expected"; break;
			case 16: s = "\"start\" expected"; break;
			case 17: s = "\"=\" expected"; break;
			case 18: s = "\"(\" expected"; break;
			case 19: s = "\")\" expected"; break;
			case 20: s = "\"if\" expected"; break;
			case 21: s = "\"check\" expected"; break;
			case 22: s = "\"case\" expected"; break;
			case 23: s = "\":\" expected"; break;
			case 24: s = "\"while\" expected"; break;
			case 25: s = "\"for\" expected"; break;
			case 26: s = "\"\"\" expected"; break;
			case 27: s = "\"board\" expected"; break;
			case 28: s = "\"cell\" expected"; break;
			case 29: s = "\"piece\" expected"; break;
			case 30: s = "\"player\" expected"; break;
			case 31: s = "\"round\" expected"; break;
			case 32: s = "\"turn\" expected"; break;
			case 33: s = "\"int\" expected"; break;
			case 34: s = "\"string\" expected"; break;
			case 35: s = "\"bool\" expected"; break;
			case 36: s = "\"[\" expected"; break;
			case 37: s = "\"]\" expected"; break;
			case 38: s = "\"+\" expected"; break;
			case 39: s = "\"-\" expected"; break;
			case 40: s = "\"*\" expected"; break;
			case 41: s = "\"/\" expected"; break;
			case 42: s = "??? expected"; break;
			case 43: s = "invalid CLUBs"; break;
			case 44: s = "invalid Definition"; break;
			case 45: s = "invalid Type"; break;
			case 46: s = "invalid Statement"; break;
			case 47: s = "invalid IdentType"; break;
			case 48: s = "invalid ControlStructure"; break;
			case 49: s = "invalid AssignStatement"; break;
			case 50: s = "invalid Op"; break;
			case 51: s = "invalid SubExpression"; break;

			default: s = "error " + n; break;
		}
		errorStream.WriteLine(errMsgFormat, line, col, s);
		count++;
	}

	public virtual void SemErr (int line, int col, string s) {
		errorStream.WriteLine(errMsgFormat, line, col, s);
		count++;
	}
	
	public virtual void SemErr (string s) {
		errorStream.WriteLine(s);
		count++;
	}
	
	public virtual void Warning (int line, int col, string s) {
		errorStream.WriteLine(errMsgFormat, line, col, s);
	}
	
	public virtual void Warning(string s) {
		errorStream.WriteLine(s);
	}
} // Errors


public class FatalError: Exception {
	public FatalError(string m): base(m) {}
}
}